package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

//	func FetchHisList(m map[string]string) {
//		defer func() {
//			if e := recover(); e != nil {
//				bc.Log("抓取失败")
//			}
//		}()
//		log.Println("start FetchList")
//		bc.Log("start FetchList")
//		list := GetMap(his, m)
//
//		now := time.Now()
//
//		var itemList []*Item
//
//		for _, item := range list {
//			realObj := item.(map[string]interface{})
//
//			floatId := realObj["id"].(float64)
//			id := int(floatId)
//
//			name := realObj["name"].(string)
//			quote := realObj["quote"]
//
//			realQuote := quote.(map[string]interface{})
//
//			usd := realQuote["USD"]
//
//			realUsd := usd.(map[string]interface{})
//
//			price := realUsd["market_cap"]
//
//			realPrice := price.(float64)
//
//			newItem := NewItem(id, name, realPrice, now)
//
//			itemList = append(itemList, newItem)
//		}
//
//		FlushList(now, itemList)
//
//		fmt.Println(itemList)
//	}
var mutex = sync.Mutex{}

var ing int64

func FetchList() {
	defer func() {
		go func() {
			time.Sleep(5 * time.Second)
			atomic.StoreInt64(&ing, 0)
			bc.Log("已解锁可抓取")
		}()
		if e := recover(); e != nil {
			bc.Log("抓取失败")
		}
	}()

	loadInt64 := atomic.LoadInt64(&ing)

	if loadInt64 == 1 {
		bc.Log("重复抓取")
		return
	}

	atomic.StoreInt64(&ing, 1)

	log.Println("开始抓取")
	bc.Log("start FetchList")

	m := make(map[string]string)

	m["sort"] = "market_cap"
	m["sort_dir"] = "desc"
	m["limit"] = "300"

	list := GetMap(list, m)

	now := time.Now()

	var itemList []*Item

	for _, item := range list {
		realObj := item.(map[string]interface{})

		floatId := realObj["id"].(float64)
		id := int(floatId)

		name := realObj["name"].(string)
		quote := realObj["quote"]

		realQuote := quote.(map[string]interface{})

		usd := realQuote["USD"]

		realUsd := usd.(map[string]interface{})

		price := realUsd["market_cap"]

		realPrice := price.(float64)

		newItem := NewItem(id, name, realPrice, now)

		itemList = append(itemList, newItem)
	}

	FlushList(now, itemList)
	bc.Log("抓取结束")
}

func PullTop300() []ListItem {
	var li []ListItem
	m := make(map[string]string)
	m["limit"] = "300"
	m["sort"] = "cmc_rank"
	r := Get(listMap, m, &li, func(s string) {
		bc.Log("抓取成功")
	})

	if r {
		return li
	}
	return nil
}

func OpenRateExcel(rateFilter func(input []*RankPair) []*RankPair, fName string) {
	_, rateData := GetCalcData("", rateFilter)

	//k -> 时间  v-> [涨幅行]
	var key []string

	for k, _ := range rateData {
		key = append(key, k)
	}

	key = getSoftArr(key)

	csv := filepath.Join(bc.OutExcel.Text, ToUnix(time.Now())+"_"+fName+".csv")
	file, _ := os.OpenFile(csv, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)

	writer := bufio.NewWriter(file)

	rankRange := rateFilter(bc.GetRange(""))
	//头
	head := "," + strings.Join(key, ",")
	writer.WriteString(head + "\n")

	sort.Slice(rankRange, func(i, j int) bool {
		return rankRange[i].Index < rankRange[j].Index
	})

	if rateFilter != nil {
		rankRange = rateFilter(rankRange)
	}

	for _, rank := range rankRange {
		line := fmt.Sprintf("%d %d-%d", rank.Index+1, int(rank.Left), int(rank.Right)) + ","

		var temp []string
		for _, time := range key {
			rows := rateData[time]

			total := 0
			for _, v := range rows {
				if rank.InRank(v.Rate) {
					total++
				}
			}

			temp = append(temp, strconv.Itoa(total))
		}
		line = line + strings.Join(temp, ",")
		writer.WriteString(line + "\n")
	}

	writer.Flush()

	bc.Log("输出目录:" + csv)
	u, e := url.Parse(filepath.Join("file:", csv))

	if e != nil {
		bc.Log(e.Error())
		return
	}

	e = btcApp.OpenURL(u)

	if e != nil {
		bc.Log(e.Error())
	}
}

func OpenLoseExcel(fileName string, output bool, filter func(input []*RankPair) []*RankPair) ([]*ChartPair, []*ChartPair) {
	csv := filepath.Join(bc.OutExcel.Text, ToUnix(time.Now())+"_"+fileName+".csv")
	file, _ := os.OpenFile(csv, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)

	writer := bufio.NewWriter(file)

	rankRange := filter(bc.GetRange(""))

	//k -> 时间  v-> [涨幅行]
	var key []string

	lost, _ := GetCalcData(fileName, filter)
	for k, _ := range lost {
		key = append(key, k)
	}

	key = getSoftArr(key)
	//头
	head := "," + strings.Join(key, ",")

	writer.WriteString(head + "\n")

	tpl := "%d / %d = %s"
	sort.Slice(rankRange, func(i, j int) bool {
		return rankRange[i].Index < rankRange[j].Index
	})

	finalLine := make(map[string][]*ExcelPair)

	for _, rank := range rankRange {
		line := fmt.Sprintf("%d %d-%d", rank.Index+1, int(rank.Left), int(rank.Right)) + ","
		var temp []string
		for _, time := range key {
			rows := lost[time]
			for _, v := range rows {
				if v.Index == rank.Index {
					result := "0"

					var calcFloat float64
					if v.Left == 0 {
						calcFloat = float64(v.Right)
					} else {
						calcFloat = float64(v.Right) / float64(v.Left)
					}
					result = fmt.Sprintf("%.2f", calcFloat)
					totalNumTemp := finalLine[time]
					totalNumTemp = append(totalNumTemp, NewExcelPair(calcFloat, rank.Index+1))
					finalLine[time] = totalNumTemp

					temp = append(temp, fmt.Sprintf(tpl, v.Right, v.Left, result))
				}
			}
		}
		line = line + strings.Join(temp, ",")
		writer.WriteString(line + "\n")
	}

	//汇总
	totalLine := ","

	var winChart []*ChartPair
	var loseChart []*ChartPair
	var totalTempResult []string
	for _, time := range key {
		t := finalLine[time]

		var tempTotalWin float64
		var tempTotalLos float64

		var ta []string
		var tat []*FinalLine
		if len(t) > 0 {
			for _, v := range t {

				if fileName == "win" || fileName == "wl" {
					if v.Key*float64(v.Value) != 0 {
						tempTotalWin = tempTotalWin + v.Key*float64(v.Value)

						ta = append(ta, fmt.Sprintf("%.2f", v.Key)+"*"+strconv.Itoa(v.Value))
					}
				}
				if fileName == "lose" || fileName == "wl" {
					if v.Key != 0 {
						tempTotalLos = tempTotalLos + v.Key
						tat = append(tat, NewFinalLine(fmt.Sprintf("%.2f", v.Key)+"*"+strconv.Itoa(v.Value), v.Value))
					}
				}
			}
		}
		temp := ""
		if len(ta) > 0 && (fileName == "win" || fileName == "wl") {
			f := tempTotalWin / float64(len(ta))

			temp = "(" + strings.Join(ta, "+") + ")/" + strconv.Itoa(len(ta)) + "=" + fmt.Sprintf("%.2f", f)

			pair := NewChartPair(time, f, time)
			winChart = append(winChart, pair)
		}
		if len(tat) > 0 && (fileName == "lose" || fileName == "wl") {
			var tatStr []string
			tatTotal := 0
			for _, v := range tat {
				tatStr = append(tatStr, v.Key)
				tatTotal = tatTotal + v.Weight
			}

			f := tempTotalLos / float64(tatTotal)

			temp = "(" + strings.Join(tatStr, "+") + ")/" + strconv.Itoa(tatTotal) + "=" + fmt.Sprintf("%.2f", f)

			pair := NewChartPair(time, f, time)
			loseChart = append(loseChart, pair)
		}

		totalTempResult = append(totalTempResult, temp)
	}
	totalLine = totalLine + strings.Join(totalTempResult, ",")
	writer.WriteString(totalLine)
	writer.Flush()

	if output {
		u, _ := url.Parse(filepath.Join("file:", csv))
		btcApp.OpenURL(u)
	}

	return winChart, loseChart
}

type FinalLine struct {
	Key    string
	Weight int
}

func NewFinalLine(key string, weight int) *FinalLine {
	return &FinalLine{Key: key, Weight: weight}
}

func OpenChart(up func(input []*RankPair) []*RankPair, down func(input []*RankPair) []*RankPair) {
	defer func() {
		if e := recover(); e != nil {
			bc.Log("打开失败")
			log.Println(e)
		}
	}()

	w, _ := OpenLoseExcel("wl", false, up)
	_, l := OpenLoseExcel("lose", false, up)

	w2, _ := OpenLoseExcel("wl", false, down)
	_, l2 := OpenLoseExcel("lose", false, down)

	GetChart(w, l, w2, l2)
}

func GetCalcData(fileName string, rateFilter func(input []*RankPair) []*RankPair) (map[string][]*Row, map[string][]*RateData) {
	dir := GetAllListDir()

	readDir, err := ioutil.ReadDir(dir)

	if err != nil {
		bc.Log("read dir fail" + err.Error())
		log.Println("read dir fail ", dir)
	}

	var dataList []time.Time
	for _, file := range readDir {
		name := file.Name()
		atoi, _ := strconv.Atoi(name)
		parse := time.Unix(int64(atoi), 0)

		dataList = append(dataList, parse)
	}

	if len(dataList) < 0 {
		bc.Log("样本数量1 无法计算")
		return nil, nil
	}

	sort.Slice(dataList, func(i, j int) bool {
		return dataList[i].Before(dataList[j])
	})

	ySize := len(dataList)

	maxStr := bc.Max.Text
	maxInt, _ := strconv.Atoi(maxStr)

	if ySize < maxInt {
		dataList = dataList[:ySize]
	}

	if ySize > maxInt {
		dataList = dataList[len(dataList)-maxInt:]
	}

	// [{id -> price},{id -> price}]
	stepData := pre(dataList)
	//	// key -> 时间点 vale -> [涨跌幅]
	tempRate := calcWithRange(stepData)

	//key -> 时间点  涨跌区间+数量
	step3 := Step3(tempRate, rateFilter)

	if fileName == "lose" {
		newLose := make(map[string][]*Row)

		for k, v := range step3 {
			var rows []*Row
			for _, rankPair := range v {
				row := NewRow(k, rankPair.Index, rankPair.Value, 1)
				rows = append(rows, row)
			}

			newLose[k] = rows
		}

		return newLose, tempRate
	}

	loseDataRow := handlerChartLine(step3)
	return loseDataRow, tempRate
}

func getSoftArr(s []string) []string {
	sort.Slice(s, func(i, j int) bool {
		t1, _ := time.Parse("2006-01-02 15:04:05", s[i])
		t2, _ := time.Parse("2006-01-02 15:04:05", s[j])
		return t1.Before(t2)
	})

	return s
}

func handlerWinLine(step3 map[string][]*RankPair) map[string]float64 {
	if len(step3) <= 1 {
		bc.Log("赚钱系数数据小于1 无法计算")
		log.Println("赚钱系数数据小于1 无法计算")
		return nil
	}
	// key -> 时间 value map(key -> index ,value -> rank)
	timeRankPairMap := make(map[string]map[int]*RankPair)

	for k, v := range step3 {
		tempMap := make(map[int]*RankPair)

		for _, v1 := range v {
			tempMap[v1.Index] = v1
		}

		timeRankPairMap[k] = tempMap
	}

	var keyArr []string
	for s := range step3 {
		keyArr = append(keyArr, s)
	}
	//时间排序
	keyArr = getSoftArr(keyArr)

	timePoint := make(map[string]float64)
	for k := range keyArr {

		var sum float64 = 0
		total := 0

		if k == 0 {
			continue
		}
		//时间点
		l := keyArr[k-1]
		r := keyArr[k]

		leftData := timeRankPairMap[l]
		rightData := timeRankPairMap[r]

		for k1, leftV := range leftData {

			if leftV.Value == 0 {
				continue
			}

			rightV := rightData[k1]

			t := (float64(rightV.Value) / float64(leftV.Value))
			t = t * float64(rightV.Index)
			sum = sum + t
			total++
		}

		if total != 0 {
			timePoint[r] = sum / float64(total)
		} else {
			timePoint[r] = 0
		}
	}

	return timePoint
}
func handlerChartLine(step3 map[string][]*RankPair) map[string][]*Row {
	if len(step3) <= 1 {
		bc.Log("砸钱系数数据小于1 无法计算")
		log.Println("砸钱系数数据小于1 无法计算")
		return nil
	}
	// key -> 时间 value map(key -> index ,value -> rank)
	timeRankPairMap := make(map[string]map[int]*RankPair)

	for k, v := range step3 {
		tempMap := make(map[int]*RankPair)

		for _, v1 := range v {
			tempMap[v1.Index] = v1
		}

		timeRankPairMap[k] = tempMap
	}

	var keyArr []string
	for s := range step3 {
		keyArr = append(keyArr, s)
	}
	//时间排序
	keyArr = getSoftArr(keyArr)

	timePoint := make(map[string][]*Row)

	for k := range keyArr {
		if k == 0 {
			continue
		}
		//时间点
		l := keyArr[k-1]
		r := keyArr[k]

		leftData := timeRankPairMap[l]
		rightData := timeRankPairMap[r]

		var tempRow []*Row
		for k1, rightV := range rightData {

			leftV := leftData[k1]

			row := NewRow(r, k1, rightV.Value, leftV.Value)

			tempRow = append(tempRow, row)
		}

		timePoint[r] = tempRow
	}

	return timePoint
}

func Step3(rate map[string][]*RateData, filter func(input []*RankPair) []*RankPair) map[string][]*RankPair {

	//k-> 时间 v ->  区间+数量
	rankPair := make(map[string][]*RankPair)
	//k -> 时间点 v -> 涨跌幅数组
	for k, v := range rate {

		rankRange := filter(bc.GetRange(k))

		for _, v1 := range v {
			//涨跌幅区间

			for _, v2 := range rankRange {
				v2.InRank(v1.Rate)
			}
		}

		rankPair[k] = rankRange
	}

	return rankPair
}

func calcWithRange(data []*Step2Data) map[string][]*RateData {
	// key -> 时间点 vale -> [涨跌幅]
	timeRateMap := make(map[string][]*RateData)
	for i := range data {
		if i == 0 {
			continue
		}

		before := data[i-1]
		now := data[i]

		beforeDetail := before.Data
		//k -> id v detail

		//某个时间点的 涨跌幅 数组
		var rateDataLis []*RateData
		for k, v := range now.Data {
			beforeItem, ok := beforeDetail[k]

			if ok {
				f := (v.Price/beforeItem.Price - 1) * 100

				rateDataLis = append(rateDataLis, NewRateData(v.Id, v.Name, f, v.time))
			}
		}

		timeRateMap[FormartTime(now.T)] = rateDataLis
	}

	return timeRateMap
}

func pre(dataList []time.Time) []*Step2Data {
	//dataList [time.Time]

	// [{id -> price},{id -> price}]
	var baseData []*Step2Data

	for _, v := range dataList {
		baseDataPath := GetListDir(ToUnix(v))
		m := GetDataMap(baseDataPath)
		t := &Step2Data{
			Data: m,
			T:    v,
		}
		baseData = append(baseData, t)
	}

	return baseData
}

func GetDataMap(s string) map[int]Item {
	file, err := ioutil.ReadFile(s)

	if err != nil {
		return nil
	}

	var dataList []Item
	err = json.Unmarshal(file, &dataList)

	if err != nil {
		fmt.Println(err)
		return nil
	}

	dataMap := make(map[int]Item)

	for _, v := range dataList {
		dataMap[v.Id] = v
	}

	return dataMap
}
